/**
 * Copyright 厦门中软海晟信息技术有限公司 版权所有 违者必究 2019
 */
package com.opencvjava.code;

import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.opencvjava.support.util.CvUtils.imshow;
import static com.opencvjava.support.util.CvUtils.mat;
import static org.opencv.core.Core.BORDER_CONSTANT;
import static org.opencv.core.Core.BORDER_DEFAULT;
import static org.opencv.imgcodecs.Imgcodecs.IMREAD_GRAYSCALE;
import static org.opencv.imgproc.Imgproc.Canny;
import static org.opencv.imgproc.Imgproc.GaussianBlur;
import static org.opencv.imgproc.Imgproc.HoughLinesP;
import static org.opencv.imgproc.Imgproc.INTER_LANCZOS4;
import static org.opencv.imgproc.Imgproc.THRESH_BINARY;
import static org.opencv.imgproc.Imgproc.THRESH_OTSU;
import static org.opencv.imgproc.Imgproc.THRESH_TRIANGLE;
import static org.opencv.imgproc.Imgproc.getRotationMatrix2D;
import static org.opencv.imgproc.Imgproc.line;
import static org.opencv.imgproc.Imgproc.threshold;
import static org.opencv.imgproc.Imgproc.warpAffine;

/**
 *
 *@author : sunzb
 *@date: 2019/12/2
 */
@Service
public class Omr1 {
    Mat src;

    public void test() {
        src = mat("piano", "1.jpg");
        Mat gray = mat("piano", "1.jpg", IMREAD_GRAYSCALE);
        if (gray.empty()) throw new RuntimeException("源图读取失败");
        imshow("源图", gray);
        Mat blur = new Mat();
        GaussianBlur(gray, blur, new Size(3, 3), 0, 0, BORDER_DEFAULT);
        Mat edges = new Mat();
        Canny(blur, edges, 50, 200, 3);
        imshow("边缘", edges);
        Mat rotate = rotate(blur, edges);
        imshow("校正", rotate);
//        Mat matRawBinary = rawBinary(rotate);
//        imshow("初步二值化", matRawBinary);
        Mat bin = new Mat();
        threshold(rotate, bin, 200, 255, THRESH_BINARY | THRESH_OTSU);
        imshow("二值化", bin);
        templateMatch(bin);
    }

    private void templateMatch(Mat bin) {
        List<Object[]> symbols = new ArrayList<>();
        Mat template1 = mat("piano/omr1", "template1.png", IMREAD_GRAYSCALE);
        threshold(template1, template1, 200, 255, THRESH_BINARY | THRESH_OTSU);
        imshow("模板1", template1);
        byte[] dataBin = new byte[(int) bin.total()];
        byte[] dataTemplate1 = new byte[(int) template1.total()];
        bin.get(0, 0, dataBin);
        template1.get(0, 0, dataTemplate1);
        byte[] dataBinInverse = inverse(dataBin);
        byte[] dataTemplate1Inverse = inverse(dataTemplate1);
        byte[] dataTemplate1InverseFlipped = flipped(dataTemplate1Inverse, template1.rows(), template1.cols());
        byte[] dataTemplate1Flipped = flipped(dataTemplate1, template1.rows(), template1.cols());
//        SDoublePlane F = convolve_general_4(input_image,flipped_template_1) + convolve_general_4(inverse_input_image,flipped_inverse_template_1);
        int[] convolve1 = convolve(dataBin, dataTemplate1Flipped);
        int[] convolve2 = convolve(dataBinInverse, dataTemplate1InverseFlipped);
        int[] convolve = new int[convolve1.length];
        for (int i = 0; i < convolve1.length; i++) convolve[i] = convolve1[i] + convolve2[i];

    }

    private int[] convolve(byte[] dataBin, byte[] dataTemplate1Flipped) {
        return new int[0];
    }

    private byte[] flipped(byte[] input, int rows, int cols) {
        byte[] output = new byte[input.length];
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                //        output[i][j]= input[input.rows()-i-1][input.cols()-j-1];
                output[row * cols + col] = input[(rows - row - 1) * cols + cols - col - 1];
            }
        }
        return output;
    }

    private byte[] inverse(byte[] input) {
        byte[] output = new byte[input.length];
        for (int i = 0; i < input.length; i++) {
            if (input[i] == 0) output[i] = -1;
            else output[i] = 0;
        }
        return output;
    }

    private Mat rawBinary(Mat rotate) {
        byte[] data = new byte[(int) rotate.total()];
        rotate.get(0, 0, data);
        int rowStart, rowEnd, colStart, colEnd, rowIndex, color, sum, avg, var, r = 7,
                colIteNum = rotate.cols() / 7, rowIteNum = rotate.rows() / 7;
        for (int j = 0; j < colIteNum; j++) {
            for (int i = 0; i < rowIteNum; i++) {
                rowStart = i * r;
                rowEnd = rowStart + r;
                colStart = j * r;
                colEnd = colStart + r;
                sum = 0;
                var = 0;
                List<Integer> unitData = new ArrayList<>();
                for (int row = rowStart; row < rowEnd; row++) {
                    rowIndex = row * rotate.cols();
                    for (int col = colStart; col < colEnd; col++) {
                        color = data[rowIndex + col] & 0xff;
                        sum += color;
                        unitData.add(color);
                    }
                }
                avg = sum / 49;
                for (int temp : unitData) {
                    var += Math.pow(temp - avg, 2);
                }
                if (var < 100) {
                    for (int row = rowStart; row < rowEnd; row++) {
                        rowIndex = row * rotate.cols();
                        for (int col = colStart; col < colEnd; col++) {
                            data[rowIndex + col] = -1;
                        }
                    }
                }
            }
        }
        Mat dst = new Mat(rotate.size(), rotate.type());
        dst.put(0, 0, data);
        return dst;
    }

    private Mat rotate(Mat blur, Mat edges) {
        Mat linesP = new Mat();
        HoughLinesP(edges, linesP, 1, Math.PI / 180, 100, 50, 10);
        List<Double> slopes = new ArrayList<>();
        if(linesP.rows() < 3) throw new RuntimeException("找不到3条直线");

        Mat cdstP = src.clone();
        for (int row = 0; row < linesP.rows(); row++) {
            double[] l = linesP.get(row, 0);
            slopes.add((Math.abs(l[0] - l[2]) < 0.0000001) ? 1000000 : ((double) (l[1]-l[3])) / (l[0]-l[2]));
            line(cdstP, new Point(l[0], l[1]), new Point(l[2], l[3]), new Scalar(0, 0, 255), 1, Imgproc.LINE_AA, 0);
        }
        imshow("直线检测", cdstP);

        Collections.sort(slopes);
        double angle = Math.atan(slopes.get(slopes.size() / 2)) * 180.0/Math.PI;
        Point pt = new Point(blur.cols() / 2.0, blur.rows() / 2.0);
        Mat r = getRotationMatrix2D(pt, angle, 1.0);
        Rect bbox = new RotatedRect(pt, blur.size(), angle).boundingRect();
        r.put(0, 2, bbox.width/2.0 - pt.x);
        r.put(1, 2, bbox.height/2.0 - pt.y);
        Mat rotate = new Mat();
        warpAffine(blur, rotate, r, bbox.size(), INTER_LANCZOS4, BORDER_CONSTANT, new Scalar(255, 255, 255));
        return rotate;
    }
}
